(* ===================================================================== *)
(** SIMULATION : module integrant, simulation du CDS  
    @author Mathieu Barbin, Ocan SANKUR
    @version samedi 17 novembre 2007 *)
(* ===================================================================== *)

(* 
   Deux cas :
   - soit le nombre de cycles souhaites a ete precise
   - soit on boucle la simulation
   
   Dans le cas ou intercepte le sigint (CTRL + C), on quitte 
   proprement la simulation.
*)

(* Anciennement, un regulateur de frequence (changé pour une console
   de controle plus générique en amout sur stdin) 

  let periode = 1. /. (float_of_int (!Inputtools.cps)) in 
  let regulateur = new Bplib.sleeper periode (typ = Traitement.Montre) in
  let ralentisseur () = regulateur#sleep in
*)

exception SigInt

(* ===================================================================== *)
(** Lancer la simulation : en fonction d'un triplet (c, i, t), c : circuit, 
    i : gestion des input c'est a dire une fonction de lecture des entrees 
    qui met a jour les entrees a chaque cycle, et t un traitement des 
    entrees-sorties (affichage, sauvegarde, etc) 
    La simulation s'effectue pendant (Some nb_cycles) ou en boucle (None)*)
(* ===================================================================== *)
let simulation typ circuit update_entrees traitement nb_cycles_option = 
  begin
    (* Prevoir l'arret de la simulation sur un sigint *)
    Sys.set_signal Sys.sigint (Sys.Signal_handle (fun _ -> raise SigInt));
    
    (* assert : Compatiblité des objets *)
    
    (* initialiser la simulation *)
    (* Preparation du traitement des sorties *)
    
    Message.message#lancement_simulation nb_cycles_option;
    update_entrees#init;
    circuit#init;
    traitement#init;
    
    let un_cycle () = 
      begin
	(* ralentisseur ();*)
        circuit#un_cycle (update_entrees#lecture);
	traitement#traite (circuit#get_entree) (circuit#get_sortie);
      end
    in
    (
      try
	begin
	  match nb_cycles_option with
	  | None ->
	      (* cette boucle infinie peut s'arreter avec une exception *)
              while true do
		un_cycle ()
              done;
	  | Some nb_cycles ->
              for i = 1 to nb_cycles do
		un_cycle ()
              done;
	end;
      with
      | SigInt -> ()
      | End_of_file -> ()
    );
    
    Message.message#quit_simulation (Some circuit#get_name);
    traitement#quit;
    circuit#quit;
    update_entrees#quit;
  end
(* ===================================================================== *)
